package com.chaodev.base.utils.security;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.*;

/**
 * 对称加密，包含DES,3DES,AES
 *
 * @author zhangb
 */
public class SymmetricHelper {
    private static final Charset CHARSET_TYPE= StandardCharsets.UTF_8;


    /**
     * 使用默认DES进行加密(DES/ECB/PKCS5Padding)
     * @param data 明文
     * @param key 秘钥
     * @return 密文
     */
    public static String encodeDES(byte[] data,byte[] key){
        return encodeEcbBase(data,key,"DES/ECB/PKCS5Padding","DES");
    }
    /**
     * 使用默认DES进行加密(DES/ECB/PKCS5Padding)
     * @param data 明文
     * @param key 秘钥
     * @return 密文
     */
    public static String encodeDES(String data,String key){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        return encodeDES(dataByte,keyByte);
    }

    /**
     * DES CBC加密模式
     * @param data 明文
     * @param key 秘钥
     * @param iv 偏移向量
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String encodeDESCbc(byte[] data,byte[] key,byte[] iv,PaddingMode paddingMode){
        String encodeMode="DES/CBC/"+paddingMode;
        return encodeCbcBase(data,key,encodeMode,"DES",iv);
    }

    /**
     * DES CBC加密模式
     * @param data 明文
     * @param key 秘钥
     * @param iv 偏移向量
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String encodeDESCbc(String data,String key,String iv,PaddingMode paddingMode){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        byte[] ivByte=iv.getBytes(CHARSET_TYPE);
        return encodeDESCbc(dataByte,keyByte,ivByte,paddingMode);
    }
    /**
     * DES ECB加密模式
     * @param data 明文
     * @param key 秘钥
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String encodeDESEcb(byte[] data,byte[] key,PaddingMode paddingMode){
        String encodeMode="DES/ECB/"+paddingMode;
        return encodeEcbBase(data,key,encodeMode,"DES");
    }
    /**
     * DES ECB加密模式
     * @param data 明文
     * @param key 秘钥
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String encodeDESEcb(String data,String key,PaddingMode paddingMode){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        return encodeDESEcb(dataByte,keyByte,paddingMode);
    }



    /**
     * 使用默认算法进行解密(DES/ECB/PKCS5Padding)
     * @param data 密文
     * @param key  秘钥
     * @return 明文
     */
    public static String decodeDES(byte[] data,byte[] key)  {
        return decodeEcbBase(data,key,"DES/ECB/PKCS5Padding","DES");
    }
    /**
     * 使用默认算法进行解密(DES/ECB/PKCS5Padding)
     * @param data 密文
     * @param key  秘钥
     * @return 明文
     */
    public static String decodeDES(String data,String key)  {
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        return decodeDES(dataByte,keyByte);
    }

    /**
     * DES CBC解密模式
     * @param data 密文
     * @param key 秘钥
     * @param iv 偏移向量
     * @param paddingMode 填充模式
     * @return 明文
     */
    public static String decodeDESCbc(byte[] data,byte[] key,byte[] iv,PaddingMode paddingMode){
        String encodeModel="DES/CBC/"+paddingMode;
        return decodeCbcBase(data,key,encodeModel,"DES",iv);
    }
    /**
     * DES CBC模式
     * @param data 明文
     * @param key 秘钥
     * @param iv 偏移向量
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String decodeDESCbc(String data,String key,String iv,PaddingMode paddingMode){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        byte[] ivByte=iv.getBytes(CHARSET_TYPE);
        return decodeDESCbc(dataByte,keyByte,ivByte,paddingMode);
    }
    /**
     * DES ECB解密模式
     * @param data 密文
     * @param key 秘钥
     * @param paddingMode 填充模式
     * @return 明文
     */
    public static String decodeDESEcb(byte[] data,byte[] key,PaddingMode paddingMode){
        String encodeMode="DES/ECB/"+paddingMode;
        return decodeEcbBase(data,key,encodeMode,"DES");
    }
    /**
     * DES ECB解密模式
     * @param data 密文
     * @param key 秘钥
     * @param paddingMode 填充模式
     * @return 明文
     */
    public static String decodeDESEcb(String data,String key,PaddingMode paddingMode){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        return decodeDESEcb(dataByte,keyByte,paddingMode);
    }

    /**
     * DES结束，3DES开始
     */
    /**
     * 使用默认DESede进行加密(DESede/ECB/PKCS5Padding)
     * @param data 明文
     * @param key 秘钥
     * @return 密文
     */
    public static String encodeDESede(byte[] data,byte[] key){
        return encodeEcbBase(data,key,"DESede/ECB/PKCS5Padding","DESede");
    }
    /**
     * 使用默认DESede进行加密(DESede/ECB/PKCS5Padding)
     * @param data 明文
     * @param key 秘钥
     * @return 密文
     */
    public static String encodeDESede(String data,String key){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        return encodeDESede(dataByte,keyByte);
    }

    /**
     * DESede CBC加密模式
     * @param data 明文
     * @param key 秘钥
     * @param iv 偏移向量
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String encodeDESedeCbc(byte[] data,byte[] key,byte[] iv,PaddingMode paddingMode){
        String encodeMode="DESede/CBC/"+paddingMode;
        return encodeCbcBase(data,key,encodeMode,"DESede",iv);
    }

    /**
     * DESede CBC加密模式
     * @param data 明文
     * @param key 秘钥
     * @param iv 偏移向量
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String encodeDESedeCbc(String data,String key,String iv,PaddingMode paddingMode){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        byte[] ivByte=iv.getBytes(CHARSET_TYPE);
        return encodeDESedeCbc(dataByte,keyByte,ivByte,paddingMode);
    }
    /**
     * DESede ECB加密模式
     * @param data 明文
     * @param key 秘钥
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String encodeDESedeEcb(byte[] data,byte[] key,PaddingMode paddingMode){
        String encodeMode="DESede/ECB/"+paddingMode;
        return encodeEcbBase(data,key,encodeMode,"DESede");
    }
    /**
     * DESede ECB加密模式
     * @param data 明文
     * @param key 秘钥
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String encodeDESedeEcb(String data,String key,PaddingMode paddingMode){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        return encodeDESedeEcb(dataByte,keyByte,paddingMode);
    }



    /**
     * 使用默认算法进行解密(DESede/ECB/PKCS5Padding)
     * @param data 密文
     * @param key  秘钥
     * @return 明文
     */
    public static String decodeDESede(byte[] data,byte[] key)  {
        return decodeEcbBase(data,key,"DESede/ECB/PKCS5Padding","DESede");
    }
    /**
     * 使用默认算法进行解密(DESede/ECB/PKCS5Padding)
     * @param data 密文
     * @param key  秘钥
     * @return 明文
     */
    public static String decodeDESede(String data,String key)  {
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        return decodeDESede(dataByte,keyByte);
    }

    /**
     * DESede CBC解密模式
     * @param data 密文
     * @param key 秘钥
     * @param iv 偏移向量
     * @param paddingMode 填充模式
     * @return 明文
     */
    public static String decodeDESedeCbc(byte[] data,byte[] key,byte[] iv,PaddingMode paddingMode){
        String encodeModel="DESede/CBC/"+paddingMode;
        return decodeCbcBase(data,key,encodeModel,"DESede",iv);
    }
    /**
     * DESede CBC模式
     * @param data 明文
     * @param key 秘钥
     * @param iv 偏移向量
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String decodeDESedeCbc(String data,String key,String iv,PaddingMode paddingMode){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        byte[] ivByte=iv.getBytes(CHARSET_TYPE);
        return decodeDESedeCbc(dataByte,keyByte,ivByte,paddingMode);
    }
    /**
     * DESede ECB解密模式
     * @param data 密文
     * @param key 秘钥
     * @param paddingMode 填充模式
     * @return 明文
     */
    public static String decodeDESedeEcb(byte[] data,byte[] key,PaddingMode paddingMode){
        String encodeMode="DESede/ECB/"+paddingMode;
        return decodeEcbBase(data,key,encodeMode,"DESede");
    }
    /**
     * DESede ECB解密模式
     * @param data 密文
     * @param key 秘钥
     * @param paddingMode 填充模式
     * @return 明文
     */
    public static String decodeDESedeEcb(String data,String key,PaddingMode paddingMode){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        return decodeDESedeEcb(dataByte,keyByte,paddingMode);
    }
    /**
     * 3DES结束，AES开始
     */
    /**
     * 使用默认AES进行加密(AES/ECB/PKCS5Padding)
     * @param data 明文
     * @param key 秘钥
     * @return 密文
     */
    public static String encodeAES(byte[] data,byte[] key){
        return encodeEcbBase(data,key,"AES/ECB/PKCS5Padding","AES");
    }
    /**
     * 使用默认AES进行加密(AES/ECB/PKCS5Padding)
     * @param data 明文
     * @param key 秘钥
     * @return 密文
     */
    public static String encodeAES(String data,String key){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        return encodeAES(dataByte,keyByte);
    }

    /**
     * AES CBC加密模式
     * @param data 明文
     * @param key 秘钥
     * @param iv 偏移向量
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String encodeAESCbc(byte[] data,byte[] key,byte[] iv,PaddingMode paddingMode){
        String encodeMode="AES/CBC/"+paddingMode;
        return encodeCbcBase(data,key,encodeMode,"AES",iv);
    }

    /**
     * AES CBC加密模式
     * @param data 明文
     * @param key 秘钥
     * @param iv 偏移向量
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String encodeAESCbc(String data,String key,String iv,PaddingMode paddingMode){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        byte[] ivByte=iv.getBytes(CHARSET_TYPE);
        return encodeAESCbc(dataByte,keyByte,ivByte,paddingMode);
    }
    /**
     * AES ECB加密模式
     * @param data 明文
     * @param key 秘钥
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String encodeAESEcb(byte[] data,byte[] key,PaddingMode paddingMode){
        String encodeMode="AES/ECB/"+paddingMode;
        return encodeEcbBase(data,key,encodeMode,"AES");
    }
    /**
     * AES ECB加密模式
     * @param data 明文
     * @param key 秘钥
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String encodeAESEcb(String data,String key,PaddingMode paddingMode){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        return encodeAESEcb(dataByte,keyByte,paddingMode);
    }



    /**
     * 使用默认算法进行解密(AES/ECB/PKCS5Padding)
     * @param data 密文
     * @param key  秘钥
     * @return 明文
     */
    public static String decodeAES(byte[] data,byte[] key)  {
        return decodeEcbBase(data,key,"AES/ECB/PKCS5Padding","AES");
    }
    /**
     * 使用默认算法进行解密(AES/ECB/PKCS5Padding)
     * @param data 密文
     * @param key  秘钥
     * @return 明文
     */
    public static String decodeAES(String data,String key)  {
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        return decodeAES(dataByte,keyByte);
    }

    /**
     * AES CBC解密模式
     * @param data 密文
     * @param key 秘钥
     * @param iv 偏移向量
     * @param paddingMode 填充模式
     * @return 明文
     */
    public static String decodeAESCbc(byte[] data,byte[] key,byte[] iv,PaddingMode paddingMode){
        String encodeModel="AES/CBC/"+paddingMode;
        return decodeCbcBase(data,key,encodeModel,"AES",iv);
    }
    /**
     * AES CBC模式
     * @param data 明文
     * @param key 秘钥
     * @param iv 偏移向量
     * @param paddingMode 填充模式
     * @return 密文
     */
    public static String decodeAESCbc(String data,String key,String iv,PaddingMode paddingMode){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        byte[] ivByte=iv.getBytes(CHARSET_TYPE);
        return decodeAESCbc(dataByte,keyByte,ivByte,paddingMode);
    }
    /**
     * AES ECB解密模式
     * @param data 密文
     * @param key 秘钥
     * @param paddingMode 填充模式
     * @return 明文
     */
    public static String decodeAESEcb(byte[] data,byte[] key,PaddingMode paddingMode){
        String encodeMode="AES/ECB/"+paddingMode;
        return decodeEcbBase(data,key,encodeMode,"AES");
    }
    /**
     * AES ECB解密模式
     * @param data 密文
     * @param key 秘钥
     * @param paddingMode 填充模式
     * @return 明文
     */
    public static String decodeAESEcb(String data,String key,PaddingMode paddingMode){
        byte[] dataByte=data.getBytes(CHARSET_TYPE);
        byte[] keyByte=key.getBytes(CHARSET_TYPE);
        return decodeAESEcb(dataByte,keyByte,paddingMode);
    }
    /**
     * 生成秘钥
     *
     * @param key
     * @param algorithm
     * @return
     */
    private static Key getKey(byte[] key, String algorithm) throws NoSuchAlgorithmException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
        keyGenerator.init(new SecureRandom(key));
        return keyGenerator.generateKey();
    }

    /**
     * ECB加密
     * @param data 明文
     * @param key 秘钥
     * @param encodeMode 加密模式
     * @param algorithm 算法：AES,DES,DESede
     * @return 密文
     */
    private static String encodeEcbBase(byte[] data,byte[] key,String encodeMode,String algorithm) {
        try {
            Key secretKey = getKey(key, algorithm);
            // 通过标准转换获取 Cipher 对象, 由该对象完成实际的加密操作
            Cipher cipher = Cipher.getInstance(encodeMode);
            // 通过加密模式、密钥，初始化 Cipher 对象
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            // 生成密文
            byte[] resultByte=cipher.doFinal(data);
            //直接返回会乱码，使用base64加密后返回
            return Base64Helper.encodeBase64(resultByte);
        }
        catch (Exception e){
            throw new RuntimeException("算法错误:"+e.getMessage());
        }
    }
    /**
     * ECB解密
     * @param data 密文
     * @param key 秘钥
     * @param encodeMode 加密模式
     * @param algorithm 算法：AES,DES,DESede
     * @return 密文
     */
    private static String decodeEcbBase(byte[] data,byte[] key,String encodeMode,String algorithm) {
        try {
            Key secretKey = getKey(key, algorithm);
            // 通过标准转换获取 Cipher 对象, 由该对象完成实际的加密操作
            Cipher cipher = Cipher.getInstance(encodeMode);
            // 通过加密模式、密钥，初始化 Cipher 对象
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            //先解密一道BASE64
            byte[] dataByte=Base64Helper.decodeBase64Byte(data);
            // 生成明文
            return  new String(cipher.doFinal(dataByte),CHARSET_TYPE);
        }
        catch (Exception e){
            throw new RuntimeException("算法错误:"+e.getMessage());
        }
    }

    /**
     * CBC加密
     * @param data 明文
     * @param key 秘钥
     * @param encodeMode 加密模式
     * @param algorithm 算法：AES,DES,DESede
     * @param iv 偏移向量
     * @return 密文
     */
    private static String encodeCbcBase(byte[] data,byte[] key,String encodeMode,String algorithm,byte[] iv) {
        try {
            Key secretKey = getKey(key, algorithm);
            // 通过标准转换获取 Cipher 对象, 由该对象完成实际的加密操作
            Cipher cipher = Cipher.getInstance(encodeMode);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
            // 通过加密模式、密钥，初始化 Cipher 对象
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
            // 生成密文
            byte[] resultByte=cipher.doFinal(data);
            //直接返回会乱码，使用base64加密后返回
            return Base64Helper.encodeBase64(resultByte);
        }
        catch (Exception e){
            throw new RuntimeException("算法错误:"+e.getMessage());
        }
    }
    /**
     * CBC解密
     * @param data 密文
     * @param key 秘钥
     * @param encodeMode 加密模式
     * @param algorithm 算法：AES,DES,DESede
     * @param iv 偏移向量
     * @return 密文
     */
    private static String decodeCbcBase(byte[] data,byte[] key,String encodeMode,String algorithm,byte[] iv) {
        try {
            Key secretKey = getKey(key, algorithm);
            // 通过标准转换获取 Cipher 对象, 由该对象完成实际的加密操作
            Cipher cipher = Cipher.getInstance(encodeMode);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
            // 通过加密模式、密钥，初始化 Cipher 对象
            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
            //先解密一道BASE64
            byte[] dataByte=Base64Helper.decodeBase64Byte(data);
            // 生成明文
            return  new String(cipher.doFinal(dataByte),CHARSET_TYPE);
        }
        catch (Exception e){
            throw new RuntimeException("算法错误:"+e.getMessage());
        }
    }
}

